The development of the EDVAC computer system of 1948 is often cited as the beginning of the computer era. Since that time, computer systems have evolved into extremely complicated devices. To be sure, today's computers are more sophisticated than early systems such as the EDVAC. Fundamentally speaking, though, the most basic requirements levied upon computer systems have not changed. Now, as in the past, a computer system's job is to access, manipulate, and store information. This fact is true regardless of the type or vintage of computer system.
Computers access, manipulate, and store information by following a detailed set of instructions (“program” or “software”) that are generally organized into a number of discrete parts (“functions,” “components” or “modules”). Software developers have organized some of the most commonly used functions into “libraries,” which the software developer can “call” when the program under development needs to use those functions. In this way, the software developers can use the functions in the library without having to rewrite the supporting code each time.
Traditionally, computer programs were distributed from the software developer to the end user on physical storage media, such as a floppy disk or a CD-ROM. Unfortunately, this method of distribution was both slow and costly. To overcome this limitation, many software developers have begun to replace the physical medial with transmission media, such as the Internet. Although this change allows end users to easily and cheaply “download” new software from the developer, the end user has no way of knowing whether the party supplying the software is trustworthy. That is, due to the public nature of the Internet, the end user cannot ensure that the digital location from which they download the software is controlled by the software developer and that the software they received was not altered during transit. Software supplied from untrusted sources can contain unexpected “bugs” and/or may contain a computer “virus” that can destroy data on the end user's computer system.
This problem is compounded by the fact that many programs require changes (“updates,” “upgrades,” or “fixes”) after the initial distribution to correct the bugs in the program and to add new functionality. As a result, computer owners also need to ensure the ongoing integrity of the software on their systems. Ongoing system integrity imposes requirements on the security system beyond those required to ensure transport integrity. More specifically, any system used to ensure ongoing system integrity of a software product must accommodate the addition of new components to an existing software installation, the replacement of existing software components with new components, and the deletion of old components.
Conventional systems typically use digital signatures to ensure software integrity. A digital signature is an electronic code that uniquely identifies the signer of a digital object (e.g., a file). There are a number of different encryption techniques that provide these features. One such technique is to first use a special function to compute a value (“hash”) on the software object in such a way that it is extremely unlikely that some other object will produce the same value, and then use public-key encryption techniques to encrypt the hash. The end user can verify both the signed object and the sender's identity by first re-computing the hash of the object, then decoding to the original hash, and finally comparing the recomputed hash with the decoded hash. Additional information about public key cryptography and digital signatures can be found in Bruce Schneir, Applied Cryptography, Second Edition Protocols, Algorithms, and Source Code in C, (John Wiley & Sons 1996), which is herein incorporated by reference in its entirety.
One problem with conventional techniques, however, is that they could not protect variant portions of the programs, such as references to an external library. For example, a program dependent on a relocateable library needs to resolve the location to call entries within the external libraries. This linkage is resolved using the explicit program signature (e.g., in C++, the so-called “mangled name” that uniquely names the procedure or method and includes the description of the call parameters in the mangled name. In other languages, this may just be a uniquely resolvable name that may include none, part, or all of the parameters involved). The resolution of the external reference, sometimes referred to as the “binding,” is left unprotected by conventional systems because the actual code that calls into the external can vary. This is, because the binding is merely a pointer to an external library whose location in memory is not dependably known, the value of the pointer can vary. Because the value of the pointer can vary, including the binding in the hashed code will cause the hash function to compute a different value.
Accordingly there is a need for a method and system that can ensure the integrity of the variant portions of programs.